package com.jsrj.service.impl;

import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.diagnosis.DiagnosisUtils;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.jsrj.common.core.domain.AjaxResult;
import com.jsrj.common.rediskey.RedisKey;
import com.jsrj.common.utils.JWTUtils;
import com.jsrj.common.wxService.WxService;
import com.jsrj.domain.Users;
import com.jsrj.dto.UsersDto;
import com.jsrj.mapper.UsersMapper;
import com.jsrj.model.AlipayAuth;
import com.jsrj.model.AlipayUserInfo;
import com.jsrj.model.WXAuth;
import com.jsrj.service.IUsersService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;


/**
 * 【微信用户登录及注册】Service业务层处理
 *
 * @author ruoyi
 * @date 2022-09-07
 */
@Service
public class UsersServiceImpl implements IUsersService {
    @Autowired
    private UsersMapper usersMapper;

    @Autowired
    private WxService wxService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Value("${wxmini.appid}")
    String appid;

    @Value("${wxmini.secrey}")
    String secret;

    @Value("${alipay.privateKey}")
    String alipay_privateKey;
    @Value("${alipay.alipayPublicKey}")
    String alipay_alipayPublicKey;
    @Value("${alipay.appid}")
    String alipay_appid;


    @Override
    public UsersDto getCode(WXAuth wxAuth) {

        //        1 拼接一个微信登陆凭证校验接口
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}" +
                "&grant_type=authorization_code";
        String replace = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", wxAuth.getCode());

//        带有 session_key   openId
        String res = HttpUtil.get(replace);
//        将返回的session_key和openid解析成JSON对象
        JSONObject jsonObject = JSON.parseObject(res);
        //进一步解析成，保存到变量
        String sessionKey = (String) jsonObject.get("session_key");
        String openIds = (String) jsonObject.get("openid");

        try {
//            解密之后是用户信息 不包含openId
            String json = wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getIv(), sessionKey);

//            将 json 解密出的用户信息存放在 usersDto 中
            UsersDto usersDto = JSON.parseObject(json, UsersDto.class);
            usersDto.setOpenId(openIds);// 添加用户的openId 到 wxUserInfo中

//           3 openId唯一标识 需要去数据库中查询是否存在  如果存在登录 不存在注册
            Users users = usersMapper.queryUserByOpenId(openIds);
            if (users == null) {
//                注册
                return this.register(usersDto, res);
            } else {
//              登录
                usersDto.setId(users.getId());//将查询出的用户信息中的ID 值赋值给usersDto
                return this.logins(usersDto, res);
            }
//        6 后端对token的验证 知道此用户是否处于登陆状态 以及是那个用户登陆的
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //    执行登录操作
    private UsersDto logins(UsersDto usersDto, String res) {
//         5 使用JWT技术生，将session_key和openid结合生成一个Token 提供给前端 用户下次访问时 携带token访问
        String token = JWTUtils.sign(res);
        usersDto.setToken(token);
        usersDto.setOpenId(null);
//        把token存入redis value存为：userDto
//        缓存
        redisTemplate.opsForValue().set(RedisKey.TOKEN+token, JSON.toJSONString(usersDto), 7, TimeUnit.DAYS);
        return usersDto;
    }

    //    执行注册操作
    private UsersDto register(UsersDto usersDto, String res) {

        Users users = new Users();
        BeanUtil.copyProperties(usersDto, users);
        this.usersMapper.insertUsers(users);
        usersDto.setId(users.getId());
        return this.logins(usersDto, res);
    }

    /**
     * 支付宝用户登录
     * @param authCode
     * @return
     */
    @Override
    public AjaxResult getAlipayToken (String authCode) throws AlipayApiException {
        // 初始化SDK
        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());

        // 构造请求参数以调用接口
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();

        System.out.println("redisTemplate.opsForValue().get(RedisKey.ALIPAY_TOKEN:"+redisTemplate.opsForValue().get(RedisKey.ALIPAY_TOKEN+authCode));
        if (redisTemplate.opsForValue().get(RedisKey.ALIPAY_TOKEN+authCode)!=null && redisTemplate.opsForValue().get(RedisKey.ALIPAY_TOKEN+authCode)!=""){
            System.out.println(0);
            String str =redisTemplate.opsForValue().get(RedisKey.ALIPAY_TOKEN+authCode);
            AlipayAuth alipayAuth = JSON.parseObject(str,AlipayAuth.class);
            // 设置刷新令牌
            request.setRefreshToken(alipayAuth.getRefreshToken());
            System.out.println("刷新令牌:"+alipayAuth.getRefreshToken());
            // 设置授权方式
            request.setGrantType("refresh_token");
        }else{
            System.out.println(1);
            // 设置授权方式
            request.setGrantType("authorization_code");
            // 设置授权码
            request.setCode(authCode);
        };

        AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
        System.out.println("response.getBody():"+response.getBody());

        if (response.isSuccess()) {
            System.out.println("调用成功");
            //将首次登录获取到的令牌存储到数据库
            redisTemplate.opsForValue().set(RedisKey.ALIPAY_TOKEN+response.getAccessToken(), JSON.toJSONString(response),Integer.parseInt(response.getReExpiresIn()), TimeUnit.SECONDS);
            AlipayUserInfoShareRequest shareRequest = new AlipayUserInfoShareRequest();
            AlipayUserInfoShareResponse shareResponse = alipayClient.execute(shareRequest,response.getAccessToken());
            if(shareResponse.isSuccess()){
                AlipayUserInfo alipayUserInfo = JSON.parseObject(JSON.toJSONString(shareResponse),AlipayUserInfo.class);
                alipayUserInfo.setAccessToken(response.getAccessToken());
                System.out.println("调用成功");
                return AjaxResult.success(alipayUserInfo);
            } else {
                return AjaxResult.error();
            }

        } else {
            System.out.println("调用失败");
            // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
            String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
            return AjaxResult.error(diagnosisUrl);
        }
    }

    /**
     * 支付宝用户登录配置
     * @return 配置信息
     */
    private AlipayConfig getAlipayConfig() {
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
        alipayConfig.setAppId(alipay_appid);
        alipayConfig.setPrivateKey(alipay_privateKey);
        alipayConfig.setAlipayPublicKey(alipay_alipayPublicKey);
        alipayConfig.setFormat("json");
        alipayConfig.setCharset("UTF-8");
        alipayConfig.setSignType("RSA2");
        return alipayConfig;
    }

    @Override
    public AjaxResult userinfo(String token, Boolean refresh) {

        /**
         * 根据token  来验证此token  是否有效
         * refresh 如果为true 代表刷新 重新生成token和redis 里面重新存储 续期
         * false直接返回用户信息 -》 redis 中查询出来  直接返回
         * 生成的token有前缀 需要把前缀转换成空
         */
        token = token.replace("Bearer", "");
        boolean verify = JWTUtils.verify(token);
        if (!verify) {
            return AjaxResult.error("未登录");
        }
        String usersJson = redisTemplate.opsForValue().get(RedisKey.TOKEN + token);
        if (StringUtils.isBlank(usersJson)) {
            return AjaxResult.error("未登录");
        }
        UsersDto usersDto = JSON.parseObject(usersJson, UsersDto.class);
        if (refresh) {
            token = JWTUtils.sign(token);
            usersDto.setToken(token);
            redisTemplate.opsForValue().set(RedisKey.TOKEN + token, JSON.toJSONString(usersDto), 7, TimeUnit.DAYS);
        }
        return AjaxResult.success(usersDto);
    }


}
